package com.lt.flashcard.model.service;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.bson.types.ObjectId;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;

import com.lt.flashcard.Utility;
import com.lt.flashcard.model.entity.Activity;
import com.lt.flashcard.model.entity.FlashcardSet;
import com.lt.flashcard.model.entity.Friendship;
import com.lt.flashcard.model.entity.FriendshipId;
import com.lt.flashcard.model.entity.Journey;
import com.lt.flashcard.model.entity.JourneyItem;
import com.lt.flashcard.model.entity.Medal;
import com.lt.flashcard.model.entity.Notification;
import com.lt.flashcard.model.entity.Role;
import com.lt.flashcard.model.entity.User;
import com.lt.flashcard.model.entity.UserConfig;

/**
 * Provide utility to interact with user account
 * 
 * @author ocsen
 * @version 1.0
 * @updated 16-Aug-2012 09:47:58
 */
public class UserService extends BasicDAO<User> {

    private static final Logger log = Logger.getLogger(UserService.class);

    public UserService() {
        super(User.class);
    }

    private com.google.code.morphia.dao.BasicDAO<UserConfig, ObjectId> _userconfigdao = null;

    /**
     * Get DAO to interact with userconfig
     * 
     * @return
     */
    private com.google.code.morphia.dao.BasicDAO<UserConfig, ObjectId> getUserConfigDAO() {
        if (_userconfigdao == null) {
            _userconfigdao = new com.google.code.morphia.dao.BasicDAO<UserConfig, ObjectId>(UserConfig.class, Repository.getInstance()
                    .getDatastore());
        }
        return _userconfigdao;
    }

    /**
     * Find user config by id
     * 
     * @param id
     * @return
     */
    public UserConfig getUserConfig(String id) {
        return getUserConfigDAO().get(new ObjectId(id));
    }

    /**
     * Find user config of a user
     * 
     * @param userid
     * @return
     */
    public UserConfig getUserConfigOfUser(Long userid) {
        com.google.code.morphia.query.Query<UserConfig> query = getUserConfigDAO().createQuery();
        query.field("userid").equal(userid);
        return query.get();
    }

    /**
     * Save a user config
     * 
     * @param config
     */
    public void saveUserConfig(UserConfig config) {
        getUserConfigDAO().save(config);
    }

    /**
     * Find user account by username
     * 
     * @param username
     * @param password
     * @return The user account or null
     */
    public User findByUsername(String username) {
        return (User) createCriteria().add(Restrictions.eq("username", username)).uniqueResult();
    }

    /**
     * Find user account by email
     * 
     * @param email
     * @return
     */
    public User findByEmail(String email) {
        if (Utility.isEmptyString(email)) {
            return null;
        }
        return (User) createCriteria().add(Restrictions.eq("email", email)).uniqueResult();
    }

    /**
     * Find list of user by username
     * 
     * @param usernames
     * @return
     */
    public List<User> findByUsername(String[] usernames) {
        if (ArrayUtils.isEmpty(usernames)) {
            return null;
        }

        Criteria crit = createCriteria();
        crit.add(Restrictions.in("username", usernames));
        crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
        return crit.list();
    }

    /**
     * Check to see if exists an account username
     * 
     * @param username
     * @return
     */
    public boolean isAccountExist(String username) {
        Criteria crit = createCriteria();
        crit.add(Restrictions.eq("username", username));
        crit.setProjection(Projections.rowCount());
        Integer c = (Integer) crit.uniqueResult();
        return c > 0;
    }

    /**
     * Check if an email account exists in the system
     * 
     * @param email
     * @return
     */
    public boolean isEmailExist(String email) {
        if (Utility.isEmptyString(email)) {
            return true;
        }
        Criteria crit = createCriteria();
        crit.add(Restrictions.eq("email", email));
        crit.setProjection(Projections.rowCount());
        Integer c = (Integer) crit.uniqueResult();
        return c > 0;
    }

    /**
     * Add friendship for user1 and user2
     * 
     * @param user1
     * @param user2
     */
    public void addFriendShip(Long uid1, Long uid2) {
        Session srv = getCurrentSession();
        Friendship friendship1 = new Friendship(new FriendshipId(uid1, uid2));
        Friendship friendship2 = new Friendship(new FriendshipId(uid2, uid1));
        Transaction trans = null;
        try {
            trans = srv.beginTransaction();
            srv.saveOrUpdate(friendship1);
            srv.saveOrUpdate(friendship2);
            trans.commit();

            // update notification
            NotificationService nsvr = Repository.getInstance().getNotificationService();
            List<Notification> lst = nsvr.findNotification(Notification.TYPE_FRIENDREQUEST, uid1, uid2);
            if (lst != null) {
                for (Notification not : lst) {
                    not.setType(Notification.TYPE_ACCEPTEDFRIENDREQUEST);
                    not.setStatus(Notification.STATUS_READ);
                    nsvr.saveNotification(not);
                }
            }
        } catch (Exception e) {
            if (trans != null) {
                trans.rollback();
            }
        }
    }

    /**
     * Remove friendship between user1 and user2
     * 
     * @param user1
     * @param user2
     */
    public void removeFriendShip(User user1, User user2) {
        // remove bi-direction
        Session srv = getCurrentSession();
        Query query = srv
                .createQuery("delete from Frienship friendship where (id.user1id = :user1id and id.user2id = :user2id) or (id.user1id = :user2id and id.user2id = :user1id)");
        query.setParameter("user1id", user1.getId());
        query.setParameter("user2id", user2.getId());
        query.executeUpdate();
    }

    /**
     * Check to see if they are friends
     * 
     * @param user1
     * @param user2
     * @return is friend
     */
    public boolean isFriend(User user1, User user2) {
        if (user1 == null || user2 == null) {
            return false;
        }
        if (user1.getId().equals(user2.getId())) {
            return true;
        }
        Session srv = getCurrentSession();
        Query query = srv.createQuery("select count(*) from Friendship where id.user1id = :user1id and id.user2id = :user2id");
        query.setParameter("user1id", user1.getId());
        query.setParameter("user2id", user2.getId());
        Long count = (Long) query.uniqueResult();
        return count > 0;
    }

    /**
     * Check if user2 is friend of friend of user2
     * 
     * @param user1
     * @param user2
     * @return
     */
    public boolean isFriendOfFriend(User user1, User user2) {
        if (user1 == null || user2 == null) {
            return false;
        }
        Session srv = getCurrentSession();
        Query query = srv
                .createQuery("select count(*) FROM Friendship friendship1, Friendship friendship2 where friendship1.id.user1id = :user1id AND friendship1.id.user2id = friendship2.id.user1id AND friendship2.id.user2id = :user2id");
        query.setParameter("user1id", user1.getId());
        query.setParameter("user2id", user2.getId());
        Long count = (Long) query.uniqueResult();
        return count > 0;
    }

    /**
     * Get list of friend of user1
     * 
     * @param user1
     * @return
     */
    public List<User> getFriendList(User user) {
        if (user == null) {
            return new ArrayList<User>();
        }
        Session srv = getCurrentSession();
        Query query = srv
                .createQuery("select user from User user, Friendship friendship where friendship.id.user1id = :user1id AND friendship.id.user2id=user.id");
        query.setParameter("user1id", user.getId());
        return query.list();
    }

    /**
     * Get list of id of friends
     * 
     * @param user
     * @return
     */
    public List<Long> getFriendIdList(Long userid) {
        try {
            Session srv = getCurrentSession();
            Query query = srv.createQuery("select friendship.id.user2id from Friendship friendship where friendship.id.user1id = :user1id");
            query.setParameter("user1id", userid);
            return query.list();
            // return new ArrayList<Long>();
        } catch (Exception e) {
            log.error("Can't get list of friend id", e);
            return new ArrayList<Long>();
        }
    }

    /**
     * Search in all user
     * 
     * @param name
     * @param searchEmail
     *            search email or not
     * @return
     */
    public List<User> searchAllUsers(String name, boolean searchEmail) {
        Criteria crit = createCriteria();
        crit.setMaxResults(100);
        crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
        crit.add(Restrictions.eq("block", 0));
        if (searchEmail) {
            crit.add(Restrictions.or(Restrictions.like("username", name, MatchMode.ANYWHERE), Restrictions.eq("email", name)));
        } else {
            crit.add(Restrictions.like("username", name, MatchMode.ANYWHERE));
        }
        crit.add(Restrictions.ne("username", "admin"));
        return crit.list();
    }

    /**
     * Search in all friends
     * 
     * @param name
     * @return
     */
    public List<User> searchAllFriends(User user, String name, boolean searchEmail) {
        List<User> friends = getFriendList(user);
        List<User> result = new ArrayList<User>();
        if (searchEmail) {
            for (User u : friends) {
                if (u.getUsername().contains(name) || u.getEmail().equals(name)) {
                    result.add(u);
                }
            }
        } else {
            for (User u : friends) {
                if (u.getUsername().contains(name)) {
                    result.add(u);
                }
            }
        }
        return result;
    }

    /**
     * Get list of friends of friends of user
     * 
     * @param user
     * @return
     */
    public List<User> getFriendsOfFriends(User user, int number) {
        Session srv = getCurrentSession();
        Query query = srv
                .createQuery("select friendship2.id.user2id FROM Friendship friendship1, Friendship friendship2 where friendship1.id.user1id = :user1id AND friendship1.id.user2id = friendship2.id.user1id AND friendship1.id.user1id != friendship2.id.user2id");
        query.setParameter("user1id", user.getId());
        query.setMaxResults(number);
        List friends = query.list();

        if (friends.size() == 0) {
            return new ArrayList<User>();
        }

        Criteria crit = createCriteria();
        crit.add(Restrictions.in("id", query.list()));
        return crit.list();
    }

    /**
     * Count the number of friends of user
     * 
     * @param user
     * @return
     */
    public Long countNumberOfFriends(User user) {
        Session srv = getCurrentSession();
        Query query = srv.createQuery("select count(*) from Friendship friendship where friendship.id.user1id = :user1id");
        query.setParameter("user1id", user.getId());
        Iterator count = query.iterate();
        return (Long) count.next();
    }

    /**
     * Count number of all users in system
     * 
     * @return
     */
    public Long countNumberOfAllUsers() {
        // TODO optimize this
        Session srv = getCurrentSession();
        Query query = srv.createQuery("select count(*) from User user");
        Iterator count = query.iterate();
        return (Long) count.next();
    }

    /**
     * Get list of user
     * 
     * @return
     */
    public List<User> getUsers() {
        Session srv = getCurrentSession();
        Query query = srv.createQuery("select user from User user where id != 1 order by username, totalBlueCoin DESC");
        return query.list();
    }

    /**
     * Get filter list of user
     * 
     * @return
     */
    public List<User> getFilterUsers(String username, String emailname) {
        Criteria crit = createCriteria();
        crit.add(Restrictions.ne("id", new Long(1)));
        if (StringUtils.isNotBlank(username)) {
            crit.add(Restrictions.like("username", username, MatchMode.ANYWHERE));
        }
        if (StringUtils.isNotBlank(emailname)) {
            crit.add(Restrictions.like("email", emailname, MatchMode.ANYWHERE));
        }
        crit.addOrder(Order.asc("username"));
        return crit.list();
    }

    /**
     * Remove user
     * 
     * @return
     */
    // public void removeUser(Long userid) {
    // Session srv = getCurrentSession();
    // Query query = srv.createQuery("delete from User user where user.id = :userid");
    // query.setParameter("userid", userid);
    // query.executeUpdate();
    // }

    @Override
    public boolean save(User entity) {
        boolean hasRoleAuthenticated = false;
        if (entity.getRoles() == null) {
            entity.setRoles(new HashSet<Role>());
        }
        for (Role role : entity.getRoles()) {
            if (role.getName().equals(Role.ROLE_AUTHENTICATED)) {
                hasRoleAuthenticated = true;
                break;
            }
        }
        if (!hasRoleAuthenticated) {
            Role authenticated = findRoleByname(Role.ROLE_AUTHENTICATED);
            entity.getRoles().add(authenticated);
        }
        try {
            if (entity.getId() != null) {
                getCurrentSession().merge(entity);
            } else {
                getCurrentSession().save(entity);
            }
            return true;
        } catch (Exception e) {
            log.error(String.format("Can't save user %s", entity.getName()), e);
            return false;
        }
    }

    /**
     * Get filter list of user
     * 
     * @return
     */
    public List<Role> getRoles() {
        Session srv = getCurrentSession();
        Query query = srv.createQuery("select role from Role role where role.name != :role1 and role.name != :role2 order by id");
        query.setParameter("role1", "authenticated");
        query.setParameter("role2", "anonymous");
        return query.list();
    }

    /**
     * Find role by name
     * 
     * @param name
     * @return The role or null
     */
    public Role findRoleByname(String rolename) {
        Session srv = getCurrentSession();
        Query query = srv.createQuery("select role from Role role where role.name = :rolename");
        query.setParameter("rolename", rolename);
        return (Role) query.uniqueResult();
    }

    /**
     * Find user account by activation code
     * 
     * @param activation
     *            code
     * @return The user account or null
     */
    public User findByActivationCode(String code) {
        Session srv = getCurrentSession();
        Query query = srv.createQuery("select user from User user where user.activation = :usr1");
        query.setParameter("usr1", code);
        return (User) query.uniqueResult();
    }

    /**
     * Check user activation
     * 
     * @param username
     * @return is activation
     */
    public boolean isActive(String username) {
        if (StringUtils.isBlank(username)) {
            return false;
        }

        Session srv = getCurrentSession();
        Query query = srv.createQuery("select user from User user where user.username = :usr1");
        query.setParameter("usr1", username);
        User u = (User) query.uniqueResult();
        if (u != null && u.getBlock() == 0) {
            return true;
        }

        return false;
    }

    /**
     * Get top 10 users in system
     * 
     * @return
     */
    public List<User> getTop10OfAll() {
        Query query = getCurrentSession().createQuery("Select user FROM User user where user.id != 1 ORDER BY user.totalBlueCoin DESC");
        query.setMaxResults(10);
        return query.list();
    }

    /**
     * Get top 10 friends of current user
     * 
     * @return
     */
    public List<User> getTop10OfFriend(User user) {
        Query query = getCurrentSession()
                .createQuery(
                        "Select user FROM User user, Friendship friendship where friendship.id.user1id = :user1id AND friendship.id.user2id=user.id and user.id != 1 ORDER BY user.totalBlueCoin DESC");
        query.setParameter("user1id", user.getId());
        query.setMaxResults(10);
        return query.list();
    }

    /**
     * Return newly created flashcard of user
     * 
     * @param user
     * @param page
     *            Current page
     * @param pagesize
     *            The page size
     * @return
     */
    public List<FlashcardSet> getRecentLearnList(User user, int page, int pagesize) {
        Query query = getCurrentSession().createQuery(
                "SELECT DISTINCT learn.flashcardset FROM Learnhistory learn WHERE learn.user = :user ORDER BY learnOn DESC");
        query.setParameter("user", user);
        query.setMaxResults(pagesize);
        query.setFirstResult(page * pagesize);
        return query.list();
    }

    /**
     * Count number of flashcardset that has just studied
     * 
     * @param user
     * @return
     */
    public long countRecentLearnList(User user) {
        Query query = getCurrentSession().createQuery(
                "SELECT count(DISTINCT learn.flashcardset) FROM Learnhistory learn WHERE learn.user = :user");
        query.setParameter("user", user);
        Object rs = query.uniqueResult();
        return rs == null ? 0 : (Long) rs;
    }

    /**
     * get user's rank base on bluecoin
     */
    // private int count = 0;
    public int getRank(User user) {
        if (user == null || user.getId() == null) {
            return 0;
        }

        // TODO optimize this
        int coin = user.getTotalBlueCoin();
        if (coin < 10) {
            return 100;
        }
        Session srv = getCurrentSession();
        Query query = srv.createQuery("select count(*) from User user where user.totalBlueCoin > :coin order by user.totalBlueCoin DESC");
        query.setParameter("coin", coin);
        long it = (Long) query.iterate().next() + 1;

        long alluser = countNumberOfAllUsers();
        int rs = Math.round((it * 100) / alluser + 1);
        return rs > 100 ? 100 : rs;
    }

    /**
     * get your friend's rank
     */
    public int getFriendRank(User user, User currUser) {
        if (user == null || user.getId() == null || currUser == null || currUser.getId() == null || user.getId().equals(currUser.getId())) {
            return 100;
        }

        int coin = user.getTotalBlueCoin();
        if (coin < 10) {
            return 100;
        }

        int rank;
        Session srv = getCurrentSession();
        Query query = srv
                .createQuery("Select user.id FROM User user, Friendship friendship where friendship.id.user1id = :user1id AND friendship.id.user2id=user.id ORDER BY user.totalBlueCoin DESC");
        query.setParameter("user1id", currUser.getId());
        List<Long> users = query.list();
        if (users.size() > 0) {
            int usersSize = users.size();
            int userSize = users.indexOf(user.getId()) + 1;
            rank = Math.round((userSize * 100) / usersSize + 1);
        } else {
            rank = 100;
        }
        return rank;
    }

    /**
     * get number of manual friends
     */
    public long getManualFriends(User user1, User user2) {
        if (user1 == null || user2 == null || user1.getId() == null || user2.getId() == null || user1.getId().equals(user2.getId())) {
            return 0;
        }
        // TODO optimize this
        Session srv = getCurrentSession();
        // Query query =
        // srv.createQuery("Select friendship FROM Friendship friendship where (friendship.id.user1id = :user1id OR friendship.id.user1id = :user2id) AND friendship.id.user2id != :user1id AND friendship.id.user2id != :user2id group by friendship.id.user2id having(friendship.id.user1id) > 1");
        Query query = srv
                .createQuery("SELECT friendship FROM Friendship friendship WHERE friendship.id.user1id = :user1id  AND friendship.id.user2id IN ( SELECT f.id.user2id FROM Friendship f WHERE f.id.user1id = :user2id )");
        query.setParameter("user1id", user1.getId());
        query.setParameter("user2id", user2.getId());
        return query.list().size();
    }

    private com.google.code.morphia.dao.BasicDAO<Activity, ObjectId> _activitydao = null;

    /**
     * Get DAO to interact with activity
     * 
     * @return
     */
    private com.google.code.morphia.dao.BasicDAO<Activity, ObjectId> getActivityDAO() {
        if (_activitydao == null) {
            _activitydao = new com.google.code.morphia.dao.BasicDAO<Activity, ObjectId>(Activity.class, Repository.getInstance()
                    .getDatastore());
        }
        return _activitydao;
    }

    /**
     * Get all activities of friends of user, order by time
     * 
     * @param userid
     * @return
     */
    public List<Activity> getAllActivitiesOfFriends(Long userid) {
        com.google.code.morphia.dao.BasicDAO<Activity, ObjectId> srv = getActivityDAO();
        com.google.code.morphia.query.Query<Activity> query = srv.createQuery();
        query.field("targetuid").equal(userid);
        query.limit(20);
        query.order("-created");
        return query.asList();
    }

    /**
     * Get activities of friends limit by size and offset
     * 
     * @param userid
     * @param size
     * @param offset
     * @return
     */
    public List<Activity> getActivitiesOfFriends(Long userid, int size, int offset) {
        com.google.code.morphia.dao.BasicDAO<Activity, ObjectId> srv = getActivityDAO();
        com.google.code.morphia.query.Query<Activity> query = srv.createQuery();
        query.field("targetuid").equal(userid);
        query.order("-created");
        query.limit(size);
        query.offset(offset);
        return query.asList();
    }

    /**
     * Activity happens when buy an item
     * 
     * @param user
     * @param productid
     */
    public void addActivityBuyItem(User user, FlashcardSet flashcardset) {
        List<Long> friends = getFriendIdList(user.getId());
        if (friends.size() > 0) {
            Activity act = new Activity();
            act.setAvatar(user.getAvatar());
            act.setMessage(String.format("<a href='%s'>%s</a> mua item <a href='%s'>%s</a>",
                    Utility.generateUrl("dashboard", user.getUsername(), null), user.getUsername(),
                    Utility.generateUrl("flashcardset", flashcardset.getId(), flashcardset.getName()), flashcardset.getName()));
            act.setRelatedid(flashcardset.getId());
            act.setType(Activity.TYPE_BUY);
            act.setUid(user.getId());
            act.setUsername(user.getUsername());

            com.google.code.morphia.dao.BasicDAO<Activity, ObjectId> dao = getActivityDAO();
            for (Long id : friends) {
                act.setId(null);
                act.setTargetuid(id);
                dao.save(act);
            }
        }
    }

    /**
     * Activity happens when user create new flashcardset
     * 
     * @param user
     * @param flashcardsetid
     */
    public void addActivityCreateSet(User user, FlashcardSet flashcardset) {
        List<Long> friends = getFriendIdList(user.getId());
        if (friends.size() > 0) {
            Activity act = new Activity();
            act.setAvatar(user.getAvatar());
            act.setMessage(String.format("<a href='%s'>%s</a> mới tạo <a href='%s'>%s</a>",
                    Utility.generateUrl("dashboard", user.getUsername(), null), user.getUsername(),
                    Utility.generateUrl("flashcardset", flashcardset.getId(), flashcardset.getName()), flashcardset.getName()));
            act.setRelatedid(flashcardset.getId());
            act.setType(Activity.TYPE_CREATESET);
            act.setUid(user.getId());
            act.setUsername(user.getUsername());

            com.google.code.morphia.dao.BasicDAO<Activity, ObjectId> dao = getActivityDAO();
            for (Long id : friends) {
                act.setId(null);
                act.setTargetuid(id);
                dao.save(act);
            }
        }
    }

    /**
     * Activity happens when user has just get new item
     * 
     * @param user
     * @param item
     */
    public void addActivityGetItem(User user, JourneyItem item) {
        List<Long> friends = getFriendIdList(user.getId());
        if (friends.size() > 0) {
            Activity act = new Activity();
            act.setAvatar(user.getAvatar());
            act.setMessage(String.format("<a href='%s'>%s</a> mới lấy item %s", Utility.generateUrl("dashboard", user.getUsername(), null),
                    user.getUsername(), item.getName()));
            act.setType(Activity.TYPE_GETITEM);
            act.setUid(user.getId());
            act.setUsername(user.getUsername());

            com.google.code.morphia.dao.BasicDAO<Activity, ObjectId> dao = getActivityDAO();
            for (Long id : friends) {
                act.setId(null);
                act.setTargetuid(id);
                dao.save(act);
            }
        }
    }

    /**
     * Activity happens when user has just get a medal
     * 
     * @param user
     * @param medal
     */
    public void addActivityGetMedal(User user, Medal medal) {
        List<Long> friends = getFriendIdList(user.getId());
        if (friends.size() > 0) {
            Activity act = new Activity();
            act.setAvatar(user.getAvatar());
            act.setMessage(String.format("<a href='%s'>%s</a> mới lấy medal %s",
                    Utility.generateUrl("dashboard", user.getUsername(), null), user.getUsername(), medal.getName()));
            act.setType(Activity.TYPE_GETMEDAL);
            act.setUid(user.getId());
            act.setUsername(user.getUsername());

            com.google.code.morphia.dao.BasicDAO<Activity, ObjectId> dao = getActivityDAO();
            for (Long id : friends) {
                act.setId(null);
                act.setTargetuid(id);
                dao.save(act);
            }
        }
    }

    /**
     * Activity happens when user begins a journey
     * 
     * @param user
     * @param journey
     */
    public void addActivityBeginJourney(User user, Journey journey) {
        List<Long> friends = getFriendIdList(user.getId());
        if (friends.size() > 0) {
            Activity act = new Activity();
            act.setAvatar(user.getAvatar());
            act.setMessage(String.format("<a href='%s'>%s</a> mới bắt đầu hành trình %s",
                    Utility.generateUrl("dashboard", user.getUsername(), null), user.getUsername(), journey.getName()));
            act.setType(Activity.TYPE_BEGINJOURNEY);
            act.setUid(user.getId());
            act.setUsername(user.getUsername());

            com.google.code.morphia.dao.BasicDAO<Activity, ObjectId> dao = getActivityDAO();
            for (Long id : friends) {
                act.setId(null);
                act.setTargetuid(id);
                dao.save(act);
            }
        }
    }

    /**
     * Activity happens when user finish a journey
     * 
     * @param user
     * @param journey
     */
    public void addActivityFinishJourney(User user, Journey journey) {
        List<Long> friends = getFriendIdList(user.getId());
        if (friends.size() > 0) {
            Activity act = new Activity();
            act.setAvatar(user.getAvatar());
            act.setMessage(String.format("<a href='%s'>%s</a> đã kết thúc hành trình %s",
                    Utility.generateUrl("dashboard", user.getUsername(), null), user.getUsername(), journey.getName()));
            act.setType(Activity.TYPE_FINISHJOURNEY);
            act.setUid(user.getId());
            act.setUsername(user.getUsername());

            com.google.code.morphia.dao.BasicDAO<Activity, ObjectId> dao = getActivityDAO();
            for (Long id : friends) {
                act.setId(null);
                act.setTargetuid(id);
                dao.save(act);
            }
        }
    }

    /**
     * Activity happens when user make friend
     * 
     * @param user
     * @param friend
     */
    public void addActivityMakeFriend(User user, User friend) {
        List<Long> friends = getFriendIdList(user.getId());
        if (friends.size() > 0) {
            Activity act = new Activity();
            act.setAvatar(user.getAvatar());
            act.setMessage(String.format("<a href='%s'>%s</a> đã kết bạn với <a href='%s'>%s</a>",
                    Utility.generateUrl("dashboard", user.getUsername(), null), user.getUsername(),
                    Utility.generateUrl("dashboard", friend.getUsername(), null), friend.getName()));
            act.setType(Activity.TYPE_MAKEFRIEND);
            act.setRelatedid(friend.getId());
            act.setUid(user.getId());
            act.setUsername(user.getUsername());

            com.google.code.morphia.dao.BasicDAO<Activity, ObjectId> dao = getActivityDAO();
            for (Long id : friends) {
                act.setId(null);
                act.setTargetuid(id);
                dao.save(act);
            }
        }
    }

    /**
     * Find user by email
     * 
     * @param email
     * @return
     */
    public User findUserByEmail(String email) {
        Session srv = getCurrentSession();
        Query query = srv.createQuery("select u from User u where u.email = :email");
        query.setParameter("email", email);
        return (User) query.uniqueResult();
    }

    public int getTotalCardsOfUser(User user) {
        try {
            String sql = "SELECT SUM(fcs.totalflashcards) FROM FlashcardSet fcs WHERE fcs.author.id = " + user.getId();
            return ((Long) getCurrentSession().createQuery(sql).iterate().next()).intValue();
        } catch (Exception e) {
            log.error("Error when counting number of total cards owned by user: ", e);
            return 0;
        }
    }
}
